/**
 * @file       signal.hpp
 * @author     WMQ
 * @date       2025/1/4
 * @brief      信号槽
 */

#ifndef SIGNAL_HPP
#define SIGNAL_HPP

#include "spin_lock.hpp"
#include "global_thread_pool.hpp"
#include <unordered_map>
#include <cstdint>
#include <forward_list>
#include <mutex>
#include <functional>

template <typename DataType>
class Signal
{
public:
    using Callback = std::function<void(const std::shared_ptr<DataType> &)>;

    Signal()
    {
        task_token_ = std::make_shared<ThreadPool::TaskToken>();
    }

    ~Signal()
    {
        task_token_->DestroyTask();
    }

    /**
     * @brief 槽函数Token
     *
     *  用于控制槽函数的生命周期
     */
    class SlotToken
    {
    public:
        /**
         * @brief 构造函数
         * @param[in] callback 槽函数
         */
        explicit SlotToken(const Callback &callback)
        {
            callback_ = callback;
        }

        ~SlotToken() {}

        /**
         * @brief 将槽函数与信号断开
         */
        void Disconnect()
        {
            spin_lock_.Lock();
            is_connect_ = false;
            spin_lock_.Unlock();
        }

        /**
         * @brief 槽函数是否与信号连接
         * @return
         *   true  连接 \n
         *   false 断开 \n
         */
        bool IsConnect()
        {
            return is_connect_;
        }

    private:
        SpinLock spin_lock_;     /**< 自旋锁 */
        bool is_connect_ = true; /**< 槽函数是否与信号连接 */
        Callback callback_;      /**< 槽函数 */

        friend Signal<DataType>;

        /**
         * @brief 执行槽函数
         * @param[in] data 信号发出的数据
         */
        void operator()(const std::shared_ptr<DataType> &data)
        {
            spin_lock_.Lock();
            if (is_connect_)
            {
                callback_(data);
            }
            spin_lock_.Unlock();
        }
    };

    /**
     * @brief 将槽函数连接到信号
     * @param[in] callback 槽函数
     * @note
     *   callback必须确保多线程执行是线程安全的
     * @return
     *   指向SlotToken实例的共享指针 \n
     */
    std::shared_ptr<SlotToken> Connect(const Callback &callback)
    {
        auto slot_token = std::make_shared<SlotToken>(callback);

        {
            std::lock_guard<std::mutex> lock(mutex_);
            slot_map_.emplace(slot_token.get(), slot_token);
        }

        return slot_token;
    }

    /**
     * @brief 将槽函数与信号断开
     * @param[in] slot_token 槽函数Token
     */
    void Disconnect(const std::shared_ptr<SlotToken> &slot_token)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        auto it = slot_map_.find(slot_token.get());
        if (slot_map_.end() != it)
        {
            (it->second)->Disconnect();
            slot_map_.erase(it);
        }
    }

    /**
     * @brief 发出信号，将会执行所有槽函数
     * @param[in] data 信号发出的数据
     * @note
     *   data指向的数据必须确保是线程安全的
     */
    void operator()(const std::shared_ptr<DataType> &data)
    {
        std::lock_guard<std::mutex> lock(mutex_);
        for (auto it = slot_map_.begin(); it != slot_map_.end();)
        {
            std::shared_ptr<SlotToken> slot_token_ptr = it->second;
            if (slot_token_ptr->IsConnect())
            {
                GlobalThreadPool::GetInstance().AddTask(task_token_, [slot_token_ptr, data]() {
                    (*slot_token_ptr)(data);
                });
                ++it;
            }
            else
            {
                it = slot_map_.erase(it);
            }
        }
    }

    Signal(const Signal &) = delete;
    Signal &operator=(const Signal &) = delete;

private:
    std::unordered_map<void*, std::shared_ptr<SlotToken>> slot_map_;
    std::mutex mutex_;
    std::shared_ptr<ThreadPool::TaskToken> task_token_ {nullptr};
};

#endif